#include "Components/ComponentOnFire.h"
#include "Components/ComponentBody.h"

#include "Network/PackageManager.h"
#include "GameSingleton.h"
#include "Network/Packages/ComponentOnFirePackage.h"
#include "GameRandom.hpp"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemTerrain.h"

#include "Blocks/WaterBlock.h"
#include "Blocks/MagmaBlock.h"
#include "Blocks/FireBlock.h"
#include "Blocks/CampfireBlock.h"

namespace PocketSurvival
{

    void ComponentOnFire::setOnFile(entt::entity attackerEntity, float theDuration)
    {
        attacker = attackerEntity;
        fireDuration = MathUtils::Max<float>(theDuration, fireDuration);
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentOnFirePackage>(this, attackerEntity));
    }

    void ComponentOnFire::update(float dt)
    {
        SubsystemTimeOfDay *subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        SubsystemTerrain *subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        if (fireDuration > 0.0f)
        {
            fireDuration = MathUtils::Max(fireDuration - dt, 0.0f);
            if (componentBody->immersionFactor > 0.5f && componentBody->immersionFluidBlock->isBlock<WaterBlock *>() == true)
            {
                extinguish();
            }
        }
        if (subsystemTimeOfDay->getGameTime() <= nextCheckTime)
        {
            return;
        }
        nextCheckTime = subsystemTimeOfDay->getGameTime() + GameSingleton::gameRandom->getFloat(0.9f, 1.1f);
        touchesFire = false;

        // CheckIfBodyTouchesFire
        {
            BoundingBox boundingBox = componentBody->getBoundingBox();
            boundingBox.Min -= 0.25f;
            boundingBox.Max += 0.25f;
            int32_t num = Terrain::ToCell(boundingBox.Min.posX);
            int32_t num2 = Terrain::ToCell(boundingBox.Min.posY);
            int32_t num3 = Terrain::ToCell(boundingBox.Min.posZ);
            int32_t num4 = Terrain::ToCell(boundingBox.Max.posX);
            int32_t num5 = Terrain::ToCell(boundingBox.Max.posY);
            int32_t num6 = Terrain::ToCell(boundingBox.Max.posZ);
            for (int32_t i = num; i <= num4; i++)
            {
                for (int32_t j = num2; j <= num5; j++)
                {
                    for (int32_t k = num3; k <= num6; k++)
                    {
                        int32_t cellValue = subsystemTerrain->getCellValue(i, j, k);
                        int32_t num7 = Terrain::ExtractContents(cellValue);
                        int32_t num8 = Terrain::ExtractData(cellValue);
                        switch (num7)
                        {
                        case FireBlock::Index():
                            if (num8 == 0)
                            {
                                auto box2 = BoundingBox(Vector3(i, j, k), Vector3(i + 1, j + 1, k + 1));
                                if (boundingBox.intersection(box2))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                                break;
                            }
                            if ((num8 & 1) != 0)
                            {
                                auto box3 = BoundingBox(Vector3(i, j, k + 0.5f), Vector3(i + 1, j + 1, k + 1));
                                if (boundingBox.intersection(box3))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                            }
                            if ((num8 & 2) != 0)
                            {
                                auto box4 = BoundingBox(Vector3(i + 0.5f, j, k), Vector3(i + 1, j + 1, k + 1));
                                if (boundingBox.intersection(box4))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                            }
                            if ((num8 & 4) != 0)
                            {
                                auto box5 = BoundingBox(Vector3(i, j, k), Vector3(i + 1, j + 1, k + 0.5f));
                                if (boundingBox.intersection(box5))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                            }
                            if ((num8 & 8) != 0)
                            {
                                auto box6 = BoundingBox(Vector3(i, j, k), Vector3(i + 0.5f, j + 1, k + 1));
                                if (boundingBox.intersection(box6))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                            }
                            break;
                        case CampfireBlock::Index():
                            if (num8 > 0)
                            {
                                auto box = BoundingBox(Vector3(i, j, k) + 0.2f, Vector3(i + 1, j + 1, k + 1) - 0.2f);
                                if (boundingBox.intersection(box))
                                {
                                    touchesFire = true;
                                    goto CheckIfBodyTouchesFireEnd;
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
    CheckIfBodyTouchesFireEnd:

        if (touchesFire == true)
        {
            ++(fireTouchCount);
            if (fireTouchCount >= 5)
            {
                setOnFile(entt::null, GameSingleton::gameRandom->getFloat(12.0f, 15.0f));
            }
        }
        else
        {
            fireTouchCount = 0;
        }
        if (componentBody->immersionFactor > 0.2f && componentBody->immersionFluidBlock->isBlock<MagmaBlock *>() == true)
        {
            setOnFile(entt::null, GameSingleton::gameRandom->getFloat(12.0f, 15.0f));
        }
    }

    static const std::string componentName = "OnFire";
    const std::string &ComponentOnFire::getName() const
    {
        return componentName;
    }
    const std::string &ComponentOnFire::GetName()
    {
        return componentName;
    }

    void ComponentOnFire::load(const nlohmann::json &jsonData)
    {
        fireDuration = jsonData["FireDuration"].get<float>();
        componentBody = GameSingleton::gameRegistry->get<ComponentBody *>(entity);
        touchesFire = false;
        fireTouchCount = 0;
        nextCheckTime = 0;
    }

    void ComponentOnFire::save(nlohmann::json &jsonData)
    {
        jsonData["FireDuration"] = fireDuration;
    }

    void ComponentOnFire::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "FireDuration", "float", fireDuration);
    }
} // namespace PocketSurvival
